import React, { useContext, useMemo, useRef } from "react";
import classNames from "classnames";
import useSize from "./utils/useSize";
import {
  MenuTreeDataType,
  MenuTreeProps,
  _MenuTreeValueItemType,
  _MenuTreeValueType,
} from "./type";
import {
  flatten,
  getAllTreeNodes,
  getLeafNodeState,
  getLeafNodes,
  hasChildren,
  replaceNodeValue,
  getTreeDepth,
} from "./utils";
import { ThemeContext } from "../theme";
import { useStyles } from "./style";
import TreeNode from "./components/Node";
import Operator from "./components/Operator";
import RenderPlaceHolder from "./components/Placeholder";

export default function MenuTree({
  treeData,
  value: propsValue = [],
  disabled = false,
  columnTitle = [],
  controlDisabledChecked = false,
  onChange,
}: MenuTreeProps) {
  const flatTree = useMemo(
    () => flatten({ tree: treeData, level: 1 }),
    [treeData]
  );

  // 被选中的叶子节点的状态
  const value = useMemo((): _MenuTreeValueType => {
    const leaves = getLeafNodes(treeData);
    return propsValue
      .filter((node) => leaves.nodes.includes(node.key))
      .map((node) => {
        const { key, title, parentKey, checkedOperators } = node;
        const cur = flatTree.find((x) => x.key === key)!;

        let isAll: boolean, isChecked: boolean;
        if (!hasChildren(cur) && cur.operators) {
          const r = getLeafNodeState(
            checkedOperators,
            leaves.record[key],
            cur.multiple
          );
          isAll = r.isAll;
          isChecked = r.isChecked;
        } else isAll = isChecked = true;

        return {
          key,
          title,
          parentKey,
          checkedOperators,
          isChecked,
          isAll,
        };
      });
  }, [propsValue, flatTree]);

  const treeDepth = useMemo(() => getTreeDepth(treeData), [treeData]);

  const ref = useRef(null);
  const size = useSize(ref);
  const itemWidth = useMemo((): string => {
    const hasOpera = !!flatTree.find(
      (node) => !hasChildren(node) && node.operators
    );
    if (size?.width)
      return `${size.width / (treeDepth + (hasOpera ? 1 : 0))}px`;
    return "100%";
  }, [size?.width, flatTree]);

  const { fontSize } = useContext(ThemeContext);
  const classes = useStyles({ fontSize });

  // 节点勾选/取消勾选事件
  function checkHandler(checked: boolean, key: string | number) {
    let newValue: _MenuTreeValueType = [];

    const node = flatTree.find((n) => n.key === key)!;
    const leaves = getLeafNodes(treeData);

    if (hasChildren(node)) {
      const curLeafs = getLeafNodes(node.children);
      const newArray = curLeafs.nodes.map((x) => {
        const n = flatTree.find((y) => y.key === x)!;
        let checkedOperators: Array<string | number> = [];
        if (!hasChildren(n) && n.operators && n.operators.length && checked) {
          checkedOperators = n.multiple
            ? curLeafs.record[x]
            : curLeafs.record[x].slice(0, 1);
        }
        let isChecked = checked;
        if (!controlDisabledChecked && n.disabled) {
          isChecked = !!value.find((t) => t.key === x)?.isChecked;
        }
        return {
          key: x,
          isAll: true,
          isChecked,
          checkedOperators,
        };
      });
      newValue = replaceNodeValue(value, newArray);
    }

    if (!hasChildren(node)) {
      const { key, operators, multiple, disabled } = node;
      let checkedOperators: Array<string | number> = [];
      if (checked && operators && operators) {
        checkedOperators = multiple
          ? leaves.record[key]
          : leaves.record[key].slice(0, 1);
      }
      let isChecked = checked;
      if (!controlDisabledChecked && disabled) {
        isChecked = !!value.find((t) => t.key === key)?.isChecked;
      }
      newValue = replaceNodeValue(value, [
        {
          key,
          isAll: true,
          isChecked,
          checkedOperators,
        },
      ]);
    }

    const { checked: checkedNodes } = getAllTreeNodes(newValue, flatTree);
    onChange?.(checkedNodes);
  }

  const renderNodes = (
    tree: MenuTreeDataType,
    level: number,
    disableChecked: boolean = false
  ) => {
    return tree.map((node, index) => {
      const current = getAllTreeNodes(value, flatTree).all.find(
        (x) => x.key === node.key
      )!;

      let content: React.ReactNode;
      const nodeDisableChecked = disabled || node.disabled || disableChecked;

      if (hasChildren(node)) {
        content = renderNodes(node.children, level + 1, nodeDisableChecked);
      } else {
        let operators: React.ReactNode = null;
        if (!hasChildren(node) && node.operators) {
          operators = (
            <span style={{ width: itemWidth }} className={classes.item}>
              <Operator
                options={node.operators}
                multiple={node.multiple}
                value={
                  value.find((x) => x.key === node.key)?.checkedOperators || []
                }
                disabled={nodeDisableChecked}
                onChange={(v) => {
                  const { isAll, isChecked } = getLeafNodeState(
                    v,
                    node.operators?.map((x) => x.value),
                    node.multiple
                  );
                  const r = replaceNodeValue(value, [
                    { key: node.key, checkedOperators: v, isAll, isChecked },
                  ]);
                  const { checked } = getAllTreeNodes(r, flatTree);
                  onChange?.(checked);
                }}
              />
            </span>
          );
        }

        if (treeDepth === level) content = operators;
        else {
          content = (
            <RenderPlaceHolder count={treeDepth - level} itemWidth={itemWidth}>
              {operators}
            </RenderPlaceHolder>
          );
        }
      }
      return (
        <div className={classNames(classes.flexWrap)}>
          <div className={classes.item} style={{ width: itemWidth }}>
            <TreeNode
              title={node.title}
              disableChecked={nodeDisableChecked}
              isChecked={current.isChecked}
              isAll={current.isAll}
              onCheck={(v) => {
                checkHandler(v, node.key);
              }}
            />
          </div>
          <div>{content}</div>
          {index + 1 !== tree.length && <b className={classes.line} />}
        </div>
      );
    });
  };

  return (
    <div className={classes.wrap} ref={ref}>
      <div className={classNames(classes.title, classes.flexWrap)}>
        {columnTitle.map((x, index) => (
          <span className={classes.item} style={{ width: itemWidth }}>
            {index === 0 && <span className={classes.placeholder} />}
            {x}
          </span>
        ))}
      </div>

      {renderNodes(treeData, 1)}
    </div>
  );
}
